#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

typedef struct Task {
    void (*function)(void *arg);
    void *arg;
    struct Task *next;
} Task;

typedef struct {
    Task *head;
    Task *tail;
    pthread_mutex_t lock;
    pthread_cond_t notify;
    int stop;
} TaskQueue;

void task_queue_init(TaskQueue *queue)
{
    queue->head = NULL;
    queue->tail = NULL;
    queue->stop = 0;
    pthread_mutex_init(&queue->lock, NULL);
    pthread_cond_init(&queue->notify, NULL);
}

void task_queue_destroy(TaskQueue *queue)
{
    pthread_mutex_lock(&queue->lock);
    Task *current = queue->head;
    while (current) {
        Task *tmp = current;
        current = current->next;
        free(tmp);
    }
    pthread_mutex_unlock(&queue->lock);
    pthread_mutex_destroy(&queue->lock);
    pthread_cond_destroy(&queue->notify);
}

void task_queue_push(TaskQueue *queue, void (*function)(void *), void *arg)
{
    Task *task = (Task *)malloc(sizeof(Task));
    task->function = function;
    task->arg = arg;
    task->next = NULL;

    pthread_mutex_lock(&queue->lock);
    if (queue->tail) {
        queue->tail->next = task;
    } else {
        queue->head = task;
    }
    queue->tail = task;
    pthread_cond_signal(&queue->notify);
    pthread_mutex_unlock(&queue->lock);
}

Task *task_queue_pop(TaskQueue *queue)
{
    pthread_mutex_lock(&queue->lock);
    while (queue->head == NULL && !queue->stop) {
        pthread_cond_wait(&queue->notify, &queue->lock);
    }
    Task *task = queue->head;
    if (task) {
        queue->head = task->next;
        if (queue->head == NULL) {
            queue->tail = NULL;
        }
    }
    pthread_mutex_unlock(&queue->lock);
    return task;
}

void *worker_thread(void *arg)
{
    TaskQueue *queue = (TaskQueue *)arg;
    while (1) {
        Task *task = task_queue_pop(queue);
        if (task == NULL && queue->stop) {
            break;
        }
        if (task) {
            task->function(task->arg);
            free(task);
        }
    }
    return NULL;
}

void sample_task(void *arg)
{
    int *num = (int *)arg;
    printf("Processing task with number: %d\n", *num);
    sleep(1);  // Simulate some work
}

int main()
{
    TaskQueue queue;
    task_queue_init(&queue);

    pthread_t thread;
    pthread_create(&thread, NULL, worker_thread, (void *)&queue);

    for (int i = 0; i < 10; ++i) {
        int *arg = malloc(sizeof(int));
        *arg = i;
        task_queue_push(&queue, sample_task, arg);
    }

    sleep(5);  // Allow some time for processing

    pthread_mutex_lock(&queue.lock);
    queue.stop = 1;
    pthread_cond_signal(&queue.notify);
    pthread_mutex_unlock(&queue.lock);

    pthread_join(thread, NULL);
    task_queue_destroy(&queue);

    return 0;
}
